﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;
using System.Diagnostics.Contracts;
using System.Collections;
using System.Globalization;
using System.Resources;

namespace YasharEl.Infrastructure.ResourcesManagement.Managers.Database
{
    /// <summary>
    /// Custom resource writer for saving resources away to a database
    /// </summary>
    public class DataResourceWriter : IResourceWriter
    {
        #region Fields

        // The resource base name.
        private string baseName;
        // The current culture name.
        private string cultureName;
        // The internal array of resources waiting to be written to the database.
        private SortedList resources;
        // Track whether Dispose has been called.
        private bool disposed;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="T:DataResourceWriter"/> class.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="baseName">the base name for the set of resources.</param>
        /// <param name="cultureInfo">The culture info.</param>
        public DataResourceWriter(string baseName, CultureInfo cultureInfo)
        {
            if (baseName == null)
                this.baseName = String.Empty;
            else
                this.baseName = baseName;

            if (cultureInfo == null)
                this.cultureName = "neutral";
            else
                this.cultureName = (cultureInfo.Name.Length == 0) ? "neutral" : cultureInfo.Name;

            resources = new SortedList();
        }

        /// <summary>
        /// Use C# destructor syntax for finalization code.
        /// </summary>
        /// <remarks>
        /// This destructor will run only if the Dispose method
        /// does not get called.
        /// It gives your base class the opportunity to finalize.
        /// Do not provide destructors in types derived from this class.
        /// </remarks>
        ~DataResourceWriter()
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(false) is optimal in terms of
            // readability and maintainability.
            Dispose(false);
        }
        #endregion

        #region Public Methods

        /// <summary>
        /// Adds a named resource of type <see cref="IResourceDataNode"></see> to the list of resources to be written.
        /// </summary>
        /// <param name="resourceDataNode">The resource data node.</param>
        /// <exception cref="T:System.ArgumentNullException">The resourceDataNode parameter is null. </exception>
        public void AddResource(IResourceDataNode resourceDataNode)
        {
            Contract.Requires<ArgumentNullException>(resourceDataNode != null, "resourceDataNode could not be null.");

            AddResource(resourceDataNode.Name, (object)resourceDataNode);
        }

        /// <summary>
        /// Adds a named resource of type <see cref="T:System.Object"></see> to the list of resources to be written.
        /// </summary>
        /// <param name="name">The name of the resource.</param>
        /// <param name="value">The value of the resource.</param>
        /// <exception cref="T:System.ArgumentNullException">The name parameter is null. </exception>
        public void AddResource(string name, object value)
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(name), "name could not be null or empty.");

            resources.Add(name, value);
        }

        /// <summary>
        /// Adds an 8-bit unsigned integer array as a named resource to the list of resources to be written.
        /// </summary>
        /// <param name="name">Name of a resource.</param>
        /// <param name="value">Value of a resource as an 8-bit unsigned integer array.</param>
        /// <exception cref="T:System.ArgumentNullException">The name parameter is null. </exception>
        public void AddResource(string name, byte[] value)
        {
            AddResource(name, (object)value);
        }

        /// <summary>
        /// Adds a named resource of type <see cref="T:System.String"></see> to the list of resources to be written.
        /// </summary>
        /// <param name="name">The name of the resource.</param>
        /// <param name="value">The value of the resource.</param>
        /// <exception cref="T:System.ArgumentException">The name parameter is null. </exception>
        public void AddResource(string name, string value)
        {
            AddResource(name, (object)value);
        }

        /// <summary>
        /// Closes the underlying resource file or stream, ensuring all the data has been written to the file.
        /// </summary>
        public void Close()
        {
            Dispose();
        }

        /// <summary>
        /// Writes all the resources added by the <see cref="M:System.Resources.IResourceWriter.AddResource(System.String,System.String)"></see> method to the output file or stream.
        /// </summary>
        public void Generate()
        {
            if (resources.Count > 0)
            {
                using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew))
                {
                    //DbCommand clearCommand = database.GetStoredProcCommand("ClearResource");
                    //database.AddInParameter(clearCommand, "@BaseName", DbType.String, baseName);
                    //database.AddInParameter(clearCommand, "@Culture", DbType.String, cultureName);

                    //database.ExecuteNonQuery(clearCommand);

                    foreach (DictionaryEntry resource in resources)
                    {
                        string typeName;
                        byte[] valueBytes;
                        object comment;

                        ResourceDataNode resourceDataNode = resource.Value as ResourceDataNode;
                        if (resourceDataNode == null)
                        {
                            typeName = resource.Value.GetType().AssemblyQualifiedName;
                            valueBytes = SerializationUtility.ToBytes(resource.Value);
                            comment = DBNull.Value;
                        }
                        else
                        {
                            if (resourceDataNode.FileRef == null)
                            {
                                typeName = resourceDataNode.TypeName;
                                valueBytes = SerializationUtility.ToBytes(resourceDataNode.Value);
                            }
                            else
                            {
                                typeName = (resourceDataNode.FileRef).GetType().AssemblyQualifiedName;
                                valueBytes = SerializationUtility.ToBytes(resourceDataNode.FileRef);
                            }
                            comment = resourceDataNode.Comment;
                        }

                        //DbCommand insertCommand = database.GetStoredProcCommand("SetResource");
                        //database.AddInParameter(insertCommand, "@BaseName", DbType.String, baseName);
                        //database.AddInParameter(insertCommand, "@Culture", DbType.String, cultureName);
                        //database.AddInParameter(insertCommand, "@Name", DbType.String, (string)resource.Key);
                        //database.AddInParameter(insertCommand, "@Type", DbType.String, typeName);
                        //database.AddInParameter(insertCommand, "@MimeType", DbType.String, DBNull.Value);
                        //database.AddInParameter(insertCommand, "@value", DbType.Binary, valueBytes);
                        //database.AddInParameter(insertCommand, "@Comment", DbType.String, comment);

                        //database.ExecuteNonQuery(insertCommand);
                    }
                    resources.Clear();
                    transactionScope.Complete();
                }
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <remarks>
        /// Implement IDisposable.
        /// Do not make this method virtual.
        /// A derived class should not be able to override this method.
        /// </remarks>
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Private Methods

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the
        // runtime from inside the finalizer and you should not reference
        // other objects. Only unmanaged resources can be disposed.
        private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    if (resources.Count > 0)
                        Generate();
                }

                // Note disposing has been done.
                disposed = true;
            }
        }
        #endregion
    }
}
